package com.inspur.gs.sbssdl.sbwhgl.sbwhby.sbwhby.front.newsbbyjh_frm.voactions;

import com.inspur.edp.bef.api.lcp.ILcpFactory;
import com.inspur.edp.bef.api.lcp.IStandardLcp;
import com.inspur.edp.bef.api.services.IBefSessionManager;
import com.inspur.edp.bff.spi.*;
import com.inspur.edp.bef.api.action.*;
import com.inspur.edp.cef.entity.changeset.ModifyChangeDetail;
import com.inspur.edp.cef.entity.condition.EntityFilter;
import com.inspur.edp.cef.entity.condition.FilterCondition;
import com.inspur.edp.cef.entity.entity.IEntityData;
import com.inspur.edp.cef.entity.entity.IValuesContainer;
import io.iec.edp.caf.commons.utils.SpringBeanUtils;

import java.time.LocalDate;
import java.time.Year;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;


public class annualPlanVOAction extends AbstractFSAction<VoidActionResult> {

    private String year;

    public annualPlanVOAction(String year) {

        this.year = year;
    }

    //保养计划id
    public final String BYJHID = "48950655-b905-4cb2-b359-9ca407386764";
    //设备台账id
    public final String SBTZID = "efd56b39-0e8d-4441-9fa1-2b019b6fd862";

    // 设备台账：id -> Device
    private Map<String, Device> deviceRegistry = new HashMap<>();

    // 历史保养计划：key=year-month-deviceId-type（简化用 List 存储）
    private List<MaintenancePlan> allPlans = new ArrayList<>();

    @Override
    public void execute() {
        /**
         * 生成下年计划
         * 1.抽取台账中配置一保、二保的设备
         * 2.根据传入的年份，根据此年保养计划生成下一年的计划 比如：2025->2026
         * 3.如果当年没有保养计划，就依次往上找保养计划 比如：2025->2024->2023
         * 4.如果这个设备找不到之前的保养计划，就自动新增保养计划
         *    		 * 初始化保养计划
         * 		 * 1.获取所有的设备信息，和今年年份
         * 		 * 2.根据设备的类级、使用日期、使用部门等排保养计划
         * 		 * 		不同类级及不同日期的保养计划不一致，A,B,C1,C2,T
         * 		 * 		A类 有三种保养计划情况 [{ 1月：1保，4月：2保 ，7月：1保，10月：2保 },{ 2月：1保，5月：2保 ，8月：1保，11月：2保 },{ 3月：1保，6月：2保 ，9月：1保，12月：2保 }]
         * 		 *		B类 有四种保养计划情况 [{ 1月：1保，5月：2保 ，9月：1保 },{ 2月：1保，6月：2保 ，10月：1保},{ 3月：1保，7月：2保 ，11月：1保 },{ 4月：1保，8月：2保 ，12月：1保 }]
         * 		 *		C1类 有四种保养计划情况 [{ 1月：1保，5月：1保 ，9月：1保 },{ 2月：1保，6月：1保 ，10月：1保},{ 3月：1保，7月：1保 ，11月：1保 },{ 4月：1保，8月：1保 ，12月：1保 }]
         * 		 *		C2类 无需保养
         * 		 *	·	T类  有六种保养计划情况 [{ 1月：1保，7月：1保 },{ 2月：1保，8月：1保 },{ 3月：1保，9月：1保 },{ 4月：1保，10月：1保 },{ 5月：1保，11月：1保 },{ 6月：1保，12月：1保 }]
         * 		 * 3.按照设备的使用部门，把设备分组，设备按照类级的保养计划情况，均衡分布在该使用部门下的这年的12个月
         * 		 * 4.如果使用日期不为空，且日期在当前时间三个月内，需要与使用日期间隔三个月进行保养计划，比如B类设备，2月入账，如果均衡分布最后是第二种计划，那他的保养计划就是6月和10月，不需要三个保养计划都占满
         *
         * 注意点：传入年份前，需先查找下一年度是否有保养计划，只要有一条设备有保养计划就不让生成
         * 防止台账中，新增的设备，在之前年份生成保养计划
         *
         * 目的：各部门的保养计划需要每个月均摊
         */

        //获取所有已有的保养计划，与传入年份对比
        SpringBeanUtils.getBean(IBefSessionManager.class).createSession();
        try {
            ILcpFactory iLcpFactory = SpringBeanUtils.getBean(ILcpFactory.class);
            IStandardLcp byjhlcp = iLcpFactory.createLcpByBEId(BYJHID);
            IStandardLcp sbtzlcp = iLcpFactory.createLcpByBEId(SBTZID);

            String nextYear = String.valueOf(Integer.parseInt(year) + 1);

            //查询传入年份下一年的保养计划，如果有计划就直接抛出异常。
            EntityFilter yearFilter = new EntityFilter();
            ArrayList<FilterCondition> yearConditions = (ArrayList<FilterCondition>) FilterCondition.builder().orEqual("year", nextYear).build();
            yearFilter.addFilterConditions(yearConditions);
            List<IEntityData> queryInfo = byjhlcp.query(yearFilter);
            if (!queryInfo.isEmpty()) {
                throw new RuntimeException(nextYear + "已有保养计划！");
            }
            //获取所有的保养计划存入集合
            List<IEntityData> byjhQuery = byjhlcp.query();
//			List<MaintenancePlan> maintenancePlanList = new ArrayList<>();
            for (IEntityData byjhItem : byjhQuery) {
                String sbId = (String) byjhItem.getValue("sbid");
                String sbbh = (String) byjhItem.getValue("sbbh");
                String sbmc = (String) byjhItem.getValue("sbmc");
                String ggxh = (String) byjhItem.getValue("ggxh");
                String xh = (String) byjhItem.getValue("xh");
                String shopName = (String) byjhItem.getValue("shopName");
                String sbbhOld = (String) byjhItem.getValue("sbbh_old");
                String lbmc = (String) byjhItem.getValue("lbmc");
                String lbbh = (String) byjhItem.getValue("lbbh");
                String ztmc = (String) byjhItem.getValue("ztmc");
                String ztbh = (String) byjhItem.getValue("ztbh");
                String abcbh = (String) byjhItem.getValue("abcbh");
                String abcmc = (String) byjhItem.getValue("abcmc");
                String bmbh = (String) byjhItem.getValue("bmbh");
                String bmmc = (String) byjhItem.getValue("bmmc");
                String year = (String) byjhItem.getValue("year");
                for (int i = 1; i <= 12; i++) {
                    String monthValue = byjhItem.getValue("month" + i).toString();
                    String month = String.valueOf(i);

                    // 只有当月份值不为空或有有效内容时才创建计划
                    if (monthValue != null && !monthValue.trim().isEmpty() && !"0".equals(monthValue)) {
                        // 根据月份值判断保养类型
//						String type = determineMaintenanceType(monthValue);
//						if (monthValue != null) {
                        MaintenancePlan plan = new MaintenancePlan(sbId, sbbh, sbmc, ggxh, xh, shopName, sbbhOld, lbmc, lbbh, ztmc, ztbh, abcbh, abcmc, bmbh, bmmc, year, month, monthValue);
                        allPlans.add(plan);
//						}
                    }
                }
            }

            //获取所有配置了保养计划的台账存入集合
            EntityFilter tzFilter = new EntityFilter();
            ArrayList<FilterCondition> tzConditions = (ArrayList<FilterCondition>) FilterCondition.builder().orEqual("del", "false").build();
            tzFilter.addFilterConditions(tzConditions);
            List<IEntityData> tzQuery = sbtzlcp.query(tzFilter);
//            List<IEntityData> tzQuery = sbtzlcp.query();
            List<Device> deviceList = new ArrayList<>();
            tzQuery.stream().forEach(s -> {
                Device device = new Device(
                        (String) s.getID(),
                        (String) s.getValue("sbmc"),
                        (String) s.getValue("sbbh"),
                        (String) s.getValue("ggxh"),
                        (String) s.getValue("xh"),
                        (String) ((IValuesContainer) s.getValue("shop_id")).getValue("shop_id_ShopName"),
                        (String) s.getValue("sbbh_old"),
                        (String) ((IValuesContainer) s.getValue("sblb_id")).getValue("sblb_id_lbmc"),
                        (String) ((IValuesContainer) s.getValue("sblb_id")).getValue("sblb_id_lbbh"),
                        (String) ((IValuesContainer) s.getValue("zczt")).getValue("zczt_ztmc"),
                        (String) ((IValuesContainer) s.getValue("zczt")).getValue("zczt_ztbh"),
                        (String) ((IValuesContainer) s.getValue("abc")).getValue("abc_lxbh"),
                        (String) ((IValuesContainer) s.getValue("abc")).getValue("abc_lxmc"),
                        (String) ((IValuesContainer) s.getValue("bmbh_id")).getValue("bmbh_id_code"),
                        (String) ((IValuesContainer) s.getValue("bmbh_id")).getValue("bmbh_id_name"),
                        s.getValue("syrq") instanceof Date ? ((Date) s.getValue("syrq")).toInstant().atZone(ZoneId.systemDefault()).toLocalDate() : null,
                        Boolean.parseBoolean(s.getValue("ry").toString()),
                        Boolean.parseBoolean(s.getValue("re").toString())
                );
                deviceList.add(device);
            });
            // 在 deviceList 构建完成后
            deviceList.forEach(d -> deviceRegistry.put(d.getSbbh(), d));
            //新保养计划
            List<MaintenancePlan> newPlans = new ArrayList<>();

            for (Device device : deviceList) {
                List<MaintenancePlan> plan = findOrCreatePlanForDevice(device, year, nextYear);
                newPlans.addAll(plan);
            }

            System.out.println("✅ 成功为 " + nextYear + " 生成 " + newPlans.size() + " 条保养计划");

            //新增保养计划,目前集合中生成的保养计划是每月分开的，现在需要根据设备编号，组成Map
            Map<String, List<MaintenancePlan>> groupedPlans = newPlans.stream()
                    .collect(Collectors.groupingBy(MaintenancePlan::getSbbh));

            for (Map.Entry<String, List<MaintenancePlan>> mapGroup : groupedPlans.entrySet()) {
                //因为每个设备下面除了保养月份和类型不同，其他一致，所以去第一个数据的值
                MaintenancePlan firstPlan = mapGroup.getValue().get(0);

                //取出相同设备编号的的保养月份和类型
                Map<String, String> monthMap = new HashMap<>();
                for (MaintenancePlan maintenancePlan : mapGroup.getValue()) {
                    String month = maintenancePlan.getMonth();
                    String type = maintenancePlan.getType();
                    monthMap.put(month, type);
                }

                String id = byjhlcp.retrieveDefault().getID();
                ModifyChangeDetail modifyChangeDetail = new ModifyChangeDetail(id);
                modifyChangeDetail.getPropertyChanges().put("year", firstPlan.getYear());
                modifyChangeDetail.getPropertyChanges().put("sbid", firstPlan.getSbId());
                modifyChangeDetail.getPropertyChanges().put("sbbh", firstPlan.getSbbh());
                modifyChangeDetail.getPropertyChanges().put("sbmc", firstPlan.getSbmc());
                modifyChangeDetail.getPropertyChanges().put("ggxh", firstPlan.getGgxh());
                modifyChangeDetail.getPropertyChanges().put("xh", firstPlan.getXh());
                modifyChangeDetail.getPropertyChanges().put("shopName", firstPlan.getShopName());
                modifyChangeDetail.getPropertyChanges().put("sbbh_old", firstPlan.getSbbhOld());
                modifyChangeDetail.getPropertyChanges().put("lbmc", firstPlan.getLbmc());
                modifyChangeDetail.getPropertyChanges().put("lbbh", firstPlan.getLbbh());
                modifyChangeDetail.getPropertyChanges().put("ztmc", firstPlan.getZtmc());
                modifyChangeDetail.getPropertyChanges().put("ztbh", firstPlan.getZtbh());
                modifyChangeDetail.getPropertyChanges().put("abcbh", firstPlan.getAbcbh());
                modifyChangeDetail.getPropertyChanges().put("abcmc", firstPlan.getAbcmc());
                modifyChangeDetail.getPropertyChanges().put("bmbh", firstPlan.getBmbh());
                modifyChangeDetail.getPropertyChanges().put("bmmc", firstPlan.getBmmc());
                for (Map.Entry<String, String> monthItem : monthMap.entrySet()) {
                    modifyChangeDetail.getPropertyChanges().put("month" + monthItem.getKey(), monthItem.getValue());
                }
                byjhlcp.modify(modifyChangeDetail);
            }
            byjhlcp.save();
        } finally {
            SpringBeanUtils.getBean(IBefSessionManager.class).closeCurrentSession();
        }

    }



    /**
     * 为单个设备查找或创建下一年的保养计划
     */
    private List<MaintenancePlan> findOrCreatePlanForDevice(Device device, String currentYear, String nextYear) {
        // 步骤1：尝试从 currentYear 往前找最近有计划的年份
        String baseYear = findLastYearWithPlan(device.getSbbh(), currentYear);

        if (baseYear != null) {
            // 复制该年的计划模式到下一年
            return copyPlansToNextYear(device, baseYear, nextYear);
        } else {
            // 该设备从未有过保养计划，需自动分配
            return createNewPlanForDevice(device, nextYear, year);
        }
    }

    /**
     * 查找该设备在指定年份及之前最近有计划的年份
     */
    private String findLastYearWithPlan(String sbbh, String startYear) {
        int startYearInt = Integer.parseInt(startYear);
        for (int year = startYearInt; year >= 2020; year--) { // 向上查5年
            String yearStr = String.valueOf(year);
            boolean hasPlan = allPlans.stream()
                    .anyMatch(p -> p.getSbbh().equals(sbbh) && p.getYear().equals(yearStr));
            if (hasPlan) {
                return yearStr;
            }
        }
        return null; // 未找到
    }

    /**
     * 将某年的计划复制到下一年（同月份）
     */
    private List<MaintenancePlan> copyPlansToNextYear(Device device, String srcYear, String targetYear) {
        return allPlans.stream()
                .filter(p -> p.getSbbh().equals(device.getSbbh()) && p.getYear().equals(srcYear))
                .map(old -> new MaintenancePlan(old.getSbId(),         // sbId
                        old.getSbbh(),
                        old.getSbmc(),
                        old.getGgxh(),
                        old.getXh(),
                        old.getShopName(),
                        old.getSbbhOld(),
                        old.getLbmc(),
                        old.getLbbh(),
                        old.getZtmc(),
                        old.getZtbh(),
                        old.getAbcbh(),
                        old.getAbcmc(),
                        old.getBmbh(),
                        old.getBmmc(),
                        targetYear,
                        old.getMonth(),
                        old.getType()
                ))
                .collect(Collectors.toList());
    }


    private String getDeviceClass(Device device) {
        String abcbh = device.getAbcbh();
        if (abcbh == null || abcbh.isEmpty()) return "C2";

        // 提取第一个字符作为类级
        if (abcbh.equals("A") || abcbh.equals("B") || abcbh.equals("C1") || abcbh.equals("T")) {
            return abcbh;
        }
        return "C2"; // 默认无需保养
    }


    /**
     * 为没有历史计划的设备创建新计划（按类级模板 + 部门内轮转分配）
     */
//    private List<MaintenancePlan> createNewPlanForDevice(Device device, String nextYear, String currentYear) {
//        List<MaintenancePlan> newPlans = new ArrayList<>();
//
//        String deviceClass = getDeviceClass(device);
//        String bmbh = device.getBmbh();
//
//        // C2类无需保养
//        if ("C2".equals(deviceClass)) {
//            return newPlans;
//        }
//
//        // 获取该部门+类级的所有设备（包括自己）
//        List<Device> deptClassDevices = deviceRegistry.values().stream()
//                .filter(d -> bmbh.equals(d.getBmbh()) && deviceClass.equals(getDeviceClass(d)))
//                .collect(Collectors.toList());
//
//        // 获取该类级的所有模板
//        List<MaintenancePattern> templates = MaintenancePlanTemplateConfig.getTemplates(deviceClass);
//        if (templates.isEmpty()) {
//            System.err.println("⚠️ 未找到设备类级 " + deviceClass + " 的保养模板，设备编号：" + device.getSbbh());
//            return newPlans;
//        }
//
//        // ✅ 核心：统计该部门+类级下，已有设备选择了哪些模板（基于历史计划）
//        Set<Integer> usedTemplateIndices = new HashSet<>();
//        for (Device d : deptClassDevices) {
//            // 只统计已有历史计划的设备
//            List<MaintenancePlan> history = allPlans.stream()
//                    .filter(p -> p.getSbbh().equals(d.getSbbh()))
//                    .collect(Collectors.toList());
//            if (!history.isEmpty()) {
//                // 推断它使用的是哪个模板（通过比对月份）
//                Integer matchedIndex = matchPlanToTemplate(history, templates);
//                if (matchedIndex != null) {
//                    usedTemplateIndices.add(matchedIndex);
//                }
//            }
//        }
//
//        // ✅ 计算新设备应使用的模板索引：轮转方式（取最小未使用索引，循环）
//        int nextTemplateIndex = 0;
//        if (!usedTemplateIndices.isEmpty()) {
//            // 找到第一个未被使用的索引（循环）
//            for (int i = 0; i < templates.size(); i++) {
//                if (!usedTemplateIndices.contains(i)) {
//                    nextTemplateIndex = i;
//                    break;
//                }
//            }
//        } else {
//            // 没有历史设备使用模板，从0开始
//            nextTemplateIndex = 0;
//        }
//
//        MaintenancePattern selectedTemplate = templates.get(nextTemplateIndex);
//
//        // 记录选择结果
//        System.out.println("🔄 新增设备 " + device.getSbbh() +
//                " (类级=" + deviceClass + ", 部门=" + bmbh + ") " +
//                "分配模板[" + nextTemplateIndex + "]: 月份=" + selectedTemplate.getMonths());
//
//        // 判断是否为近期（3个月内）入账设备，需跳过前三个月
//        boolean isRecentUseDate = isDeviceRecentlyInService(device, nextYear);
//
//        // 根据选定模板生成计划
//        for (int i = 0; i < selectedTemplate.getMonths().size(); i++) {
//            int month = selectedTemplate.getMonths().get(i);
//            String type = selectedTemplate.getTypes().get(i);
//
//            // 若为近期入账设备，跳过前3个月的保养
//            if (isRecentUseDate && month < 4) {
//                continue;
//            }
//
//            MaintenancePlan plan = new MaintenancePlan(
//                    device.getSbId(),
//                    device.getSbbh(),
//                    device.getSbmc(),
//                    device.getGgxh(),
//                    device.getXh(),
//                    device.getShopName(),
//                    device.getSbbhOld(),
//                    device.getLbmc(),
//                    device.getLbbh(),
//                    device.getZtmc(),
//                    device.getZtbh(),
//                    device.getAbcbh(),
//                    device.getAbcmc(),
//                    device.getBmbh(),
//                    device.getBmmc(),
//                    nextYear,
//                    String.valueOf(month),
//                    type
//            );
//            newPlans.add(plan);
//        }
//
//        return newPlans;
//    }

    /**
     * 为没有历史计划的设备创建新计划（按类级模板 + 部门内均摊分配）
     */
    private List<MaintenancePlan> createNewPlanForDevice(Device device, String nextYear, String currentYear) {
        List<MaintenancePlan> newPlans = new ArrayList<>();

        String deviceClass = getDeviceClass(device);
        String bmbh = device.getBmbh();

        // C2类无需保养
        if ("C2".equals(deviceClass)) {
            return newPlans;
        }

        // 获取该类级的所有模板
        List<MaintenancePattern> templates = MaintenancePlanTemplateConfig.getTemplates(deviceClass);
        if (templates.isEmpty()) {
            System.err.println("⚠️ 未找到设备类级 " + deviceClass + " 的保养模板，设备编号：" + device.getSbbh());
            return newPlans;
        }

        // ✅ 找到该部门已有的计划（历史 + 新生成）
        List<MaintenancePlan> deptPlans = allPlans.stream()
                .filter(p -> bmbh.equals(p.getBmbh()))
                .collect(Collectors.toList());

        // 按月份统计负载情况
        Map<Integer, Long> monthLoad = deptPlans.stream()
                .collect(Collectors.groupingBy(p -> Integer.parseInt(p.getMonth()), Collectors.counting()));

        // ✅ 遍历模板，选择最均衡的一个
        int bestTemplateIndex = 0;
        long bestScore = Long.MAX_VALUE;

        for (int i = 0; i < templates.size(); i++) {
            MaintenancePattern template = templates.get(i);
            long score = 0;
            for (Integer month : template.getMonths()) {
                score += monthLoad.getOrDefault(month, 0L);
            }
            if (score < bestScore) {
                bestScore = score;
                bestTemplateIndex = i;
            }
        }

        MaintenancePattern selectedTemplate = templates.get(bestTemplateIndex);

        // 记录选择结果
        System.out.println("🔄 新增设备 " + device.getSbbh() +
                " (类级=" + deviceClass + ", 部门=" + bmbh + ") " +
                "分配模板[" + bestTemplateIndex + "]: 月份=" + selectedTemplate.getMonths());

        // ✅ 判断是否为近期（3个月内）入账设备，需跳过前三个月
        boolean isRecentUseDate = isDeviceRecentlyInService(device, nextYear);

        // ✅ 根据选定模板生成计划
        for (int i = 0; i < selectedTemplate.getMonths().size(); i++) {
            int month = selectedTemplate.getMonths().get(i);
            String type = selectedTemplate.getTypes().get(i);

            // 若为近期入账设备，跳过前3个月的保养
            if (isRecentUseDate && month < 4) {
                continue;
            }

            MaintenancePlan plan = new MaintenancePlan(
                    device.getSbId(),
                    device.getSbbh(),
                    device.getSbmc(),
                    device.getGgxh(),
                    device.getXh(),
                    device.getShopName(),
                    device.getSbbhOld(),
                    device.getLbmc(),
                    device.getLbbh(),
                    device.getZtmc(),
                    device.getZtbh(),
                    device.getAbcbh(),
                    device.getAbcmc(),
                    device.getBmbh(),
                    device.getBmmc(),
                    nextYear,
                    String.valueOf(month),
                    type
            );
            newPlans.add(plan);

            // ⚠️ 更新部门负载，避免同一个方法内多个新设备挤到同一模板
            monthLoad.put(month, monthLoad.getOrDefault(month, 0L) + 1);

            // ⚠️ 把新计划放进 allPlans，让后续设备能感知
            allPlans.add(plan);
        }

        return newPlans;
    }


    /**
     * 根据设备的历史保养计划，匹配最可能的模板索引
     */
    private Integer matchPlanToTemplate(List<MaintenancePlan> history, List<MaintenancePattern> templates) {
        // 提取该设备的所有保养月份
        Set<Integer> planMonths = history.stream()
                .map(p -> Integer.parseInt(p.getMonth()))
                .collect(Collectors.toSet());

        for (int i = 0; i < templates.size(); i++) {
            MaintenancePattern template = templates.get(i);
            Set<Integer> templateMonths = new HashSet<>(template.getMonths());
            if (planMonths.equals(templateMonths)) {
                return i;
            }
        }

        // 容错：允许部分月份缺失（比如只做了部分保养）
        int bestMatchIndex = -1;
        int maxMatchCount = 0;
        for (int i = 0; i < templates.size(); i++) {
            MaintenancePattern template = templates.get(i);
            long matchCount = template.getMonths().stream()
                    .filter(planMonths::contains)
                    .count();
            if (matchCount > maxMatchCount) {
                maxMatchCount = (int) matchCount;
                bestMatchIndex = i;
            }
        }

        return maxMatchCount > 0 ? bestMatchIndex : null;
    }

    /**
     * 判断设备是否在最近3个月内入账（截止到 nextYear 的1月1日）
     * 假设 Device 有 getUseDate() 返回 LocalDate
     */
    private boolean isDeviceRecentlyInService(Device device, String nextYear) {
        LocalDate useDate = device.getUseDate();
        if (useDate == null) return false;

        // 当前系统时间
        LocalDate now = LocalDate.now();

        // 计算从 useDate 到 now 是否 ≤3 个月
        long monthsBetween = ChronoUnit.MONTHS.between(useDate, now);
        return monthsBetween <= 3;
    }
}
